package com.wei.czz.framework.security.filter;

import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.SlideWindowPo;
import com.wei.czz.common.utils.HttpUtils;
import com.wei.czz.framework.common.service.AsyncService;
import com.wei.czz.framework.common.helper.LimitHelper;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.Optional;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2022-05-02 09:22:53
 * className: RateLimitingFilter 接口限流过滤器
 * version: 1.0
 * description:
 */
@Order(11)
@Component
@AllArgsConstructor
public class RateLimitingFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger(RateLimitingFilter.class);

    private final LimitHelper limitHelper;

    private final AsyncService asyncService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
//        log.info("接口限流-前置拦截-{}", request.getRequestURI());

        // 获取请求的接口路径
        String url = request.getRequestURI();

        long now = System.currentTimeMillis();

        try {
            /*
                获取滑动窗口对象
             */
            SlideWindowPo slideWindowPo = limitHelper.get(url);
            if (Objects.nonNull(slideWindowPo)) {
                // 先获取值再进行判断，防止两次判断过程中，值变化而导致发生问题
                Long unLockTime = slideWindowPo.getUnLockTime();
                if (Objects.nonNull(unLockTime) && now < unLockTime) {
                    log.info("请求接口已被限流。url={}", url);
                    String message = Optional.ofNullable(slideWindowPo.getMessage())
                            .filter(StringUtils::isNotBlank)
                            .orElse(ResultEnum.LIMIT_FAIL.getMsg());

                    // 被限流，直接返回
                    throw new CzzException(ResultEnum.LIMIT_FAIL.getCode(), message);
                }
            }

            asyncService.execute(() -> {
                // 请求限流计算
                limitHelper.requestMath(url);
            });

        } catch (CzzException e) {
            HttpUtils.setResponse(request, response, e.getCode(), e.getMessage());

            return;
        } catch (Exception e) {
            log.error("限流计算发生异常。message={}", e.getMessage(), e);
            // 设置返回结果
            HttpUtils.setResponse(request, response, ResultEnum.SERVICE_FAIL.getCode(),
                    ResultEnum.SERVICE_FAIL.getMsg());
            return;
        }

        try {

            // 请求放行
            filterChain.doFilter(request, response);

        } catch (Exception e) {
            log.error("接口限流计算下游逻辑发生异常。message={}", e.getMessage(), e);
            // 设置返回结果
            HttpUtils.setResponse(request, response, ResultEnum.SERVICE_FAIL.getCode(),
                    ResultEnum.SERVICE_FAIL.getMsg());
        }
    }
}
